PROGRAM MBGetSInfo;

(*********************************************************************



 File     : MBGetSInfo.p
 Author   : GN		8/13/86
 Modified : Mark Baumwell 02/28/89



Well, here is a cheesy sample that checks the slots, and displays a
partial summary of what is in the slots. It doesn't go as far as getting
to the vendor info, that is something I'll fix up later.  It will show
you how to use the slot manager, though.

The user interface is hideous, of course.  It uses writelns, doesn't
update, etc.  I'd shoot myself if I tried to ship this, but right now,
it is an in house experiment only.

Basically, the slot manager is very flexible.  So much so, that a given
call may not the right one, just one that works.  Since you should never
hard code to a slot number, or even a board id (since you may rev the
board someday, and still want your software to be compatible, typically
you ask the slot manager for sResources, and it will tell you which
slot it finds them in.	An sResource can be thought of as a function
the card can perform.  The board id happens to be the most basic case
of functions - that of being a board. For instance, if you wrote a
terminal app, you don't want to have your app go out and look for one
board id.  That locks you into one hardware product. Your app could
better ask the slot manager for an sResource that had an
sRsrc_Type of, say,

 CatCommunication, TypModem, someDrSWid, someDrHwid

(you can mask out any type, if you don't care what it is)

That gets you to a card that can perform the function of a modem.  There
are two more fields to the sRsrc_Type, a software driver field and a
hardware driver field. If you wanted to check to see if this modem was
Hayes compatible, and a driver id had been defined for the Hayes command
set, you would check addtionally for the the software driver field, say,

 CatCommunication, TypModem, DrSWHayes, someDrHwid

(in other words specify the Cat, Type, DrSw, and mask out the DrHW)

Your app shouldn't care what the underlying hardware is, unless it
absolutely had to so you could be compatible with future revs of the
board, and even other manufacturer's boards (say, other Hayes compatible
modem cards). You win by not having to rev your app and by acutally
expanding the market, by appealing to more customers.

We keep the database of the values for the Category, Type, Sw and
HW ids, as well as board ids.  We don't make any values public
unless the board is shipping, though.

If you have a dedicated app that must pound on a given card, that
is a different matter. Hopefully, though cards have a driver
that you can call without having to worry about the hardware. If
a card manufacturer defines a software interface and publishes
it (for example the Hayes compatible software set), then other
developers can develop products for it.


Shell commands:

pasmat -t 3 -entab -r -u -: {MPW}MBGetsInfo.p Internal:MPW:MBGetSInfo.pasmatted -o 120

derez MBGetsInfo -s CODE :RIncludes:Types.r :RIncludes:SysTypes.r > MBGetSInfo.r
Rez types.r mbGetsInfo.r -o mbGetsInfo

pascal mbGetsInfo.p
Link -ma EqualString=equalstring ¶
 mbGetsInfo.p.o ¶
 "{Libraries}"Interface.o ¶
 "{Libraries}"Runtime.o ¶
 "{PLibraries}"Paslib.o ¶
  "{MBLibraries}"wlw.p.o ¶
  "{MBLibraries}"wlwDriver.c.o ¶
 -o mbGetsInfo
mbGetsInfo


*********************************************************************)

	USES Memtypes,Quickdraw,OSIntf,ToolIntf,PackIntf,wlw;

	CONST
		SlotManagerTrap = $6E;
		UnImplementedTrap = $9F;

		AppleID = 128; {Resource/menu IDs for Apple and File menus.}
		FileID = 129;

		AppleM = 1; {Index for each menu in MyMenus.}
		FileM = 2;

		MenuCount = 2; {Total number of menus.}

		Slot9Cmd = 1; {Menu item numbers identifying commands in file menu.}
		SlotACmd = 2;
		SlotBCmd = 3;
		SlotCCmd = 4;
		SlotDCmd = 5;
		SlotECmd = 6;
		QuitCmd = 8;
		CR = TRUE;
		NoCR = FALSE;

		WindowID = 128; {Resource ID for application's window}

		ErrID130 = 130; {Resource ID for slot manager error numbers}
		ErrID131 = 131;
		ErrID132 = 132;
		ErrID133 = 133;
		ErrID134 = 134;
		ErrID135 = 135;

		myNameID = 2; {srsrc_Name id}

	VAR
		MyMenus: ARRAY [1..MenuCount] OF MenuHandle; {Array of handles to the menus.}
		DragRect: Rect; {Rectangle used to mark boundaries for dragging window}
		MyEvent: EventRecord; {Information about an event.}
		WRecord: WindowRecord; {Information about the application window.}
		MyWindow: WindowPtr; {Pointer to WRecord.}
		WhichWindow: WindowPtr; {Pointer to window in which mouse button was pressed.}

		{writeln window variable stuff}
		numLines,numCharsPerLine: INTEGER;
		myLeft, myTop, myRight, myBottom: integer;
		bounds: Rect;
		windowTitle: Str255;
		goAway: BOOLEAN;
		visible: BOOLEAN;
		outputFont,outputSize: INTEGER;

		theChar: Char;
		Slot: signedByte;
		theErr: OSErr;
		mySpBlock: SpBlock;
		myspBlkPtr: SpBlockPtr;
		mySInfoRecord: sInfoRecord;
		mysInfoRecPtr: sInfoRecPtr;
		PrefixString: Str255;
		myFHeaderRecPtr: FHeaderRecPtr;
		myFHeaderRec: FHeaderRec;

		QuitFlag: BOOLEAN;

		ErrorString: Str255;
		Index: INTEGER;

		wRect: Rect;


	PROCEDURE GoDebugger;
		INLINE $A9FF;
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Dive into the debugger;
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

	PROCEDURE WriteSMError(ErrorPrompt: Str255; theErr: OSErr);
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 WriteSMError : Given an error, write out the prompt
		  string, then look up the errors corresponding
		 error string (located in a string resource)
		and write it out.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

		BEGIN {WriteSMError}
			write(ErrorPrompt);
			IF (theErr <= - 290) AND (theErr >= - 293) THEN {-290 to -293 range are valid error numbers}
				BEGIN
				Index := abs(theErr) - 289; {calculate resource id for err string}
				GetIndString(ErrorString, {output: returned string}
								 ErrID130, {input : resource id for string LIST (in .r file)}
								 Index); {input : index}
			END
			ELSE IF (theErr <= - 300) AND (theErr >= - 320) THEN BEGIN
				Index := abs(theErr) - 299; {calculate resource id for err string}
				GetIndString(ErrorString, {output: returned string}
								 ErrID131, {input : resource id for string LIST (in .r file)}
								 Index); {input : index}
			END
			ELSE IF (theErr <= - 330) AND (theErr >= - 351) THEN BEGIN
				Index := abs(theErr) - 329; {calculate resource id for err string}
				GetIndString(ErrorString, {output: returned string}
								 ErrID132, {input : resource id for string LIST (in .r file)}
								 Index); {input : index}
			END
			ELSE IF (theErr = - 360) THEN BEGIN
				Index := 1; {calculate resource id for err string}
				GetIndString(ErrorString, {output: returned string}
								 ErrID133, {input : resource id for string LIST (in .r file)}
								 Index); {input : index}
			END
			ELSE IF (theErr = - 400) THEN BEGIN
				Index := 1; {calculate resource id for err string}
				GetIndString(ErrorString, {output: returned string}
								 ErrID134, {input : resource id for string LIST (in .r file)}
								 Index); {input : index}
			END
			ELSE IF (theErr <= - 500) AND (theErr >= - 502) THEN BEGIN
				Index := abs(theErr) - 499; {calculate resource id for err string}
				GetIndString(ErrorString, {output: returned string}
								 ErrID135, {input : resource id for string LIST (in .r file)}
								 Index); {input : index}
			END
			ELSE BEGIN
				NumToString(theErr,ErrorString);
				write('(',ErrorString,')');
				ErrorString := ' some unkown error';
			END;
			writeln(ErrorString);
		END; {WriteSMError}

	PROCEDURE DecToHex(theDecNo: LONGINT; NoOfPlacesToPrint: INTEGER; PlaceHolder: Char);
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 DecToHex : Convert a decimal number to a hex number and
  write it out to the specified number of
  places, and padded with the specified
  placeholder character.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

		VAR
			Rem: ARRAY [1..12] OF LONGINT; {4 digits max for constant}
			i,Index: INTEGER;

		BEGIN
			IF theDecNo < 0 THEN write('-');
			theDecNo := abs(theDecNo);
			i := 0; {reset counter}
			REPEAT
				i := i + 1; {count how many digits will be needed to printed}
				Rem[i] := theDecNo MOD 16; {hold remainder}
				theDecNo := theDecNo DIV 16; {divide down after keeping remainder}
			UNTIL theDecNo = 0;
			FOR Index := 1 TO NoOfPlacesToPrint - i DO write(PlaceHolder);

			FOR Index := i DOWNTO 1 DO BEGIN
				CASE Rem[Index] OF
					10:
						write('A');
					11:
						write('B');
					12:
						write('C');
					13:
						write('D');
					14:
						write('E');
					15:
						write('F');
					OTHERWISE BEGIN
						write(Rem[Index]: 1);
					END; {otherwise}
				END; {case}
			END; {for}
		END; {Procedure DecToHex}

	PROCEDURE WritecStr(sListPtr: Ptr; PrefixStr: Str255; Id: signedByte; CR: BOOLEAN);
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 WritecStr : Given a pointer to an sList, and an id of
  a cString in the sList, write out the
  cString.	That the id points to a cString
  has to be known beforehand.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

		VAR
			cStrPtr: Ptr;
			pStr: Str255;
			i: INTEGER;

		BEGIN
			pStr := '';
			WITH mySpBlock DO BEGIN
				spID := Id; {name id}
			END;
			myspBlkPtr := @mySpBlock; {point to block}
			theErr := sGetcString(myspBlkPtr);
			IF theErr = 0 THEN BEGIN
				i := 1;
				cStrPtr := Ptr(myspBlkPtr^.spResult);
				WHILE cStrPtr^ <> 0 DO BEGIN
					pStr[i] := Char(cStrPtr^);
					cStrPtr := POINTER(ORD4(cStrPtr) + 1);
					i := i + 1;
				END;
				pStr[0] := chr(i - 1); {correct for off by one}
				write(PrefixStr,pStr);
				IF CR THEN writeln;
			END
			ELSE BEGIN {got an error}
	 {commented out to avoid messy error statements,
	  espically when getting non-existent vendor info
	  like the serial number and date}
				{WriteSMError('    Error doing sGetcString: ',theErr);}
				{Exit(WritecStr);}
			END;
		END;

	PROCEDURE DspsRsrcs(Slot,Id: INTEGER);
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 DisplaysResources : Display all the sResources of the
  card in the specified slot.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

		BEGIN
			writeln;
			writeln(' List of sResources:');
		END;

	PROCEDURE DisplaySlotInfo(SlotNumber: INTEGER);
(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 DisplaySlotInfo : Display slot info.
 routines used:

theErr := sNextTypesRsrc(myspBlkPtr); {find info only about a specified sResource type}
	 INPUT:
	 mySpBlock.spSlot 	 - slot number to start from
	 mySpBlock.Id		    - sResource list id number to start from
	 mySpBlock.spExtDev	 -
	 mySpBlock.spTBMask	 - mask for desired sResource types
	 mySpBlock.spCategory - sResource type fields
	 mySpBlock.spCType	 - sResource type fields
	 mySpBlock.spDrvrSW	 - sResource type fields
	 mySpBlock.spDrvrSW	 - sResource type fields
	 mySpBlock.spHWDev	 -
	 RETURNED:
	 mySpBlock.spSlot 	 - slot number of FOUND sResource
	 mySpBlock.Id		    - sResource list id number
	 mySpBlock.spExtDev	 -
	 mySpBlock.spsPointer -
	 mySpBlock.spRefNum	 - driver reference number
	 mySpBlock.spIOReserved - slot resource table ioReserved field
	 mySpBlock.spCategory	- sResource type fields
	 mySpBlock.spCType	 - sResource type fields
	 mySpBlock.spDrvrSW	 - sResource type fields
	 mySpBlock.spDrvrSW	 - sResource type fields
	 mySpBlock.spHWDev	 -

theErr := sRsrcInfo(myspBlkPtr); {usually used to get driver refnum}
	 INPUT:
	 mySpBlock.spSlot 	- slot number
	 mySpBlock.Id		- id of the sResource
	 mySpBlock.spExtDev	 - external device identifier
	 RETURNED:
	 mySpBlock.spsPointer	- sResource list pointer
	 mySpBlock.spCategory	- sResource type fields
	 mySpBlock.spCType	 - sResource type fields
	 mySpBlock.spDrvrSW	 - sResource type fields
	 mySpBlock.spDrvrSW	 - sResource type fields
	 mySpBlock.spRefNum	 - driver reference number
	 mySpBlock.spIOReserved - slot resource table ioReserved field
	 mySpBlock.spHWDev	 -

theErr :=  SReadWord(myspBlkPtr);
	  INPUT:
	  mySpBlock.spsPointer - pointer to the sResource list
	  mySpBlock.spID	 - id within that sResource list
	  RETURNED:
	  mySpBlock.spResult  - the 16 bit value returned in low-order word of spResult (spResult=long)

theErr :=  SReadFHeader(myspBlkPtr);
	  INPUT:
	  mySpBlock.spSlot	- slot number
	  mySpBlock.spResult  - point to result area (which is an FHeaderRec)
	  RETURNED:
	  mySpBlock.spResult  -
	  mySpBlock.spResult is a pointer to FHeader record, with the following structure:
	  FHeaderRec = PACKED RECORD
		fhDirOffset: LONGINT;	{offset to directory}
		fhLength: LONGINT;	 {length of ROM}
		fhCRC: LONGINT;	  {CRC}
		fhROMRev: SignedByte;	{revision of ROM}
		fhFormat: SignedByte;	{format - 2}
		fhTstPat: LONGINT;	 {test pattern}
		fhReserved: SignedByte;   {reserved}
		fhByteLanes: SignedByte;  {ByteLanes}
		END;

	  and of course,
	  FHeaderRecPtr = ^FHeaderRec;

theErr :=  SReadDrvrName(myspBlkPtr);
	  INPUT:
	  mySpBlock.spSlot	- slot number
	  mySpBlock.spID	 - id within that sResource list
	  mySpBlock.spResult  - the 16 bit value returned in low-order word of spResult (spResult=long)
	  RETURNED:
	  mySpBlock.spResult  - the 16 bit value returned in low-order word of spResult (spResult=long)

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*)

		TYPE
			FHeaderRecPtr = ^FHeaderRec; {have to know what the dereferenced}
			{thing points at.  If you point at}
			{(by de-referencing), a type that}
			{isn't already defined, }
			{you have to invent the pointer type}

		VAR
			theInt: INTEGER;
			tempLongInt: LONGINT;
			NameStr: Str255;
			TempID: INTEGER;

		BEGIN
			writeln;
			write('SLOT INFORMATION for Slot ');
			DecToHex(SlotNumber,1,' ');
			writeln(':');

			{read the format block}
			WITH mySpBlock DO BEGIN
				spSlot := SlotNumber;
				spResult := LONGINT(@myFHeaderRec);
			END; {with}
			myspBlkPtr := @mySpBlock; {point to block}
			theErr := SReadFHeader(myspBlkPtr);
			IF theErr = 0 THEN BEGIN {write out header block}
				WITH FHeaderRecPtr(mySpBlock.spResult)^ DO BEGIN
					write('    fhDirOffset =  ',fhDirOffset);
					write('     ');
					DecToHex(fhDirOffset,8,'0');
					writeln('  hex');

					write('    fhLength =  ','   ',fhLength); write('     ');
					DecToHex(fhLength,8,'0');
					writeln('  hex');

					write('    fhCRC =  ','    ',fhCRC); write('     ');
					DecToHex(fhCRC,8,'0');
					writeln('  hex');

					write('    fhROMRev =  ','   ',fhROMRev); write('     ');
					tempLongInt := fhROMRev;
					DecToHex(fhROMRev,2,'0');
					writeln('  hex');

					write('    fhFormat =  ','   ',fhFormat); write('     ');
					tempLongInt := fhFormat;
					DecToHex(fhFormat,2,'0');
					writeln('  hex');

					write('    fhTstPat =  ',' ',fhTstPat); write('     ');
					DecToHex(fhTstPat,8,'0');
					writeln('  hex');

					write('    fhReserved =  ',' ',fhReserved); write('     ');
					tempLongInt := fhReserved;
					DecToHex(fhReserved,2,'0');
					writeln('  hex');

					tempLongInt := BAnd(ord(fhByteLanes),$000000FF);
					write('    fhByteLanes = ',' ',tempLongInt); write('     ');
					DecToHex(tempLongInt,2,'0');
					writeln('  hex');
				END; {with}
			END {noerr}
			ELSE BEGIN {got an error}
				WriteSMError('    Error doing SReadFHeader: ',theErr);
				Exit(DisplaySlotInfo);
			END; {got an error}

 {read the driver name for a slot.	However, SReadDrvrName just gets
 the srsrc_Name out of the specified sResource, and stuffs a period
 in front of it and converts it to a pascal string.
 ****  Right now this is hard-coded for sResource id 128.
	Do this for every sResource in a slot? *** }

			WITH mySpBlock DO BEGIN
				spSlot := SlotNumber;
				TempID := 128; {really the spID}
				IF TempID > 127 THEN TempID := 256 - TempID;
				spID := TempID;
				spResult := ord(@NameStr); {point to string}
			END; {with}
			myspBlkPtr := @mySpBlock; {point to block}
			theErr := SReadDrvrName(myspBlkPtr);
			IF theErr = 0 THEN
				writeln('    The driver name is: ',NameStr)
			ELSE BEGIN {got an error}
				WriteSMError('    Error doing SReadDrvrName: ',theErr);
				Exit(DisplaySlotInfo);
			END; {got an error}

			{find board resource}
			WITH mySpBlock DO BEGIN
				spSlot := SlotNumber;
				spID := 0;
				spExtDev := 0;
				spCategory := 1; {CatBoard = $0001 for board sResource (only)} {use consts!}
				spCType := 0; {TypBoard = $0000 for board sResource (only)}
				spDrvrSw := 0; {DrvrSw = $0000 for board sResource (only)}
				spDrvrHw := 0; {DrvrSw = $0000 for board sResource (only)}
			END; {with}
			myspBlkPtr := @mySpBlock; {point to block}
			theErr := sNextTypesRsrc(myspBlkPtr); {READ BOARD RESOURCE FOR GIVEN SLOT}
			IF theErr = 0 THEN BEGIN {write out board sResource name}
				(*theErr := sRsrcInfo(myspBlkPtr);*)
				IF theErr = 0 THEN BEGIN
					{find board id}
					{use returned values: spSlot, Id, and spExtDev from above sNextTypesRsrc call}
					mySpBlock.spID := 32; {board id offset is 32}
					theErr := SReadWord(myspBlkPtr); {use returned values: spsPointer, spID from sRsrcInfo call}
					IF theErr = 0 THEN BEGIN {write out board id}
						write('    The board id is: ');
						theInt := mySpBlock.spResult;
						DecToHex(theInt,4,'0');
						write(' (hex)');
						writeln;
						write('    This board is: ');
						PrefixString := '';
						WritecStr(mySpBlock.spsPointer,PrefixString,myNameID,CR); {get the srsrc_Name}
						writeln;
						{now do vender info}
						WITH mySpBlock DO BEGIN
							spID := 36; {vendor info}
						END; {with}
						myspBlkPtr := @mySpBlock; {point to block}
						theErr := sFindStruct(myspBlkPtr);
						IF theErr = 0 THEN BEGIN
							WritecStr(mySpBlock.spsPointer,'    Vendor id is  : ',1,CR);
							WritecStr(mySpBlock.spsPointer,'    Serial Num is : ',2,CR);
							WritecStr(mySpBlock.spsPointer,'    Rev Level is  : ',3,CR);
							WritecStr(mySpBlock.spsPointer,'    Part Num is   : ',4,CR);
							WritecStr(mySpBlock.spsPointer,'    Date is       : ',5,CR);
						END
						ELSE BEGIN
							WriteSMError('    Error doing sFindStruct: ',theErr);
							{Exit(DisplaySlotInfo);}
						END;

					END; {write out board id}
				END {write out board sResource name}
				ELSE BEGIN
					WriteSMError('    Error doing SReadWord: ',theErr);
					Exit(DisplaySlotInfo);
				END;
			END {write out board sResource name}
			ELSE BEGIN
				WriteSMError('    Error doing sNextTypesRsrc: ',theErr);
				Exit(DisplaySlotInfo);
			END;
		END;

	PROCEDURE DoCommand(MResult: LONGINT);
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 DoCommand : Execute command specified by MResult (the
  result of MenuSelect).
  *** DO: ADD ABOUT BOX ***
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

		VAR
			Name: Str255; {Desk accessory name.}
			TheItem: INTEGER; {Menu item number from MResult low-order word.}
			TheMenu: INTEGER; {Menu number from MResult high-order word.}
			Temp: INTEGER;

		BEGIN {DoCommand}
			TheItem := LoWord(MResult);
			TheMenu := HiWord(MResult);

			CASE TheMenu OF {menu ID.}
			{---- Apple ID ----}
				AppleID: BEGIN
					GetItem(MyMenus[AppleM],TheItem,Name);
					Temp := OpenDeskAcc(Name);
				END;

				{---- File ID ----}
				FileID:
					IF TheItem IN [Slot9Cmd..SlotECmd] THEN BEGIN
						CASE TheItem OF {Menu item}
							Slot9Cmd:
								Slot := 9;
							SlotACmd:
								Slot := $A;
							SlotBCmd:
								Slot := $B;
							SlotCCmd:
								Slot := $C;
							SlotDCmd:
								Slot := $D;
							SlotECmd:
								Slot := $E;
						END; {Of item case}
						DisplaySlotInfo(Slot)
					END
					ELSE
						QuitFlag := TRUE;

			END; {OF menu case}
			HiliteMenu(0)
		END; {DoCommand}

	PROCEDURE DisplaySummary;
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 DisplaySummary : Display the slot information record
	for all the slots.  Uses the fact that
	the (required) board resource ALWAYS has
	 an sRsrc_Type of CatBoard, TypeBoard,0,0.
  The other sRsrc_Types will have different
  Cat, Typ, drvrsw, and drvrhw.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

		CONST
			NoOfPlacesToPrint = 1;
			PlaceHolder = '0';

		VAR
			SlotNumber: INTEGER;

		BEGIN {proc}
			writeln;
			writeln('SLOT SUMMARY:');

			FOR SlotNumber := $9 TO $E DO BEGIN {for}
				write('    Slot number  ',' ');
				DecToHex(SlotNumber,NoOfPlacesToPrint,PlaceHolder);
				write(' : ');
				WITH mySpBlock DO BEGIN
					spSlot := SlotNumber;
					mysInfoRecPtr := @mySInfoRecord;
					spResult := LONGINT(mysInfoRecPtr);
				END; {with}
				myspBlkPtr := @mySpBlock; {point to block}
				theErr := sReadInfo(myspBlkPtr); {READ SLOT INFO FOR GIVEN SLOT}
				IF theErr = 0 THEN BEGIN {no err}
					IF mySInfoRecord.siInitStatusA <> 0 THEN BEGIN {there was some init error}
						WriteSMError('got initialization error : ',mySInfoRecord.siInitStatusA);
					END
					ELSE BEGIN {init ok}
						WITH mySpBlock DO BEGIN
							spSlot := SlotNumber;
							spID := 0;
							spExtDev := 0;
							spCategory := 1; {CatBoard = $0001 for board sResource (only)}
							spCType := 0;    {TypBoard = $0000 for board sResource (only)}
							spDrvrSw := 0;   {DrvrSw   = $0000 for board sResource (only)}
							spDrvrHw := 0;   {DrvrSw   = $0000 for board sResource (only)}
						END; {with}
						myspBlkPtr := @mySpBlock; {point to block}
						theErr := sNextTypesRsrc(myspBlkPtr); {READ BOARD RESOURCE FOR GIVEN SLOT}
						IF theErr = 0 THEN BEGIN {write out board sResource name}
							PrefixString := '';
							WritecStr(mySpBlock.spsPointer,PrefixString,myNameID,CR); {use name id?}
						END {write out board sResource name}
						ELSE BEGIN
							WriteSMError('    Error doing sNextTypesRsrc: ',theErr);
							Exit(DisplaySummary);
						END
					END; {init ok}
				END {no err}
				ELSE BEGIN {error}
					WriteSMError('    Error doing sReadInfo: ',theErr);
					Exit(DisplaySummary);
				END; {error}
			END; {for}
		END; {proc}

	PROCEDURE GrayEmptySlots;
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 GrayEmptySlots : This procedure loops through the
	slots, graying out the menu items for
	any empty slots.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

		VAR
			SlotNumber: INTEGER;

		BEGIN {proc}
			FOR SlotNumber := $9 TO $E DO BEGIN {for}
				WITH mySpBlock DO BEGIN
					mysInfoRecPtr := @mySInfoRecord; {sReadInfo will fill this record}
					spResult := LONGINT(mysInfoRecPtr);
					spSlot := SlotNumber;
				END; {with}
				myspBlkPtr := @mySpBlock; {point to the block}

				theErr := sReadInfo(myspBlkPtr); {Read the slot info for the GIVEN SLOT; put}
				{the returned record info into the space}
				{pointed to by spResult.  Assy: A0 has address of spBlock}
				IF theErr = 0 THEN BEGIN {no err, therefore mysInfoRecord filled in, now see if slot is empty}
					IF mySInfoRecord.siInitStatusA = smEmptySlot THEN {WHAT IF ERROR??? should gray slot too}
						DisableItem(MyMenus[FileM],SlotNumber - 8);
				END
				ELSE BEGIN {no err}
					WriteSMError('    Error doing sReadInfo: ',theErr);
				END
			END {for}
		END; {proc}

	PROCEDURE SetUpMenus;
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 SetUpMenus : Set up menus and menu bar.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

		VAR
			i: INTEGER;

		BEGIN
			MyMenus[AppleM] := GetMenu(AppleID); {Read Apple menu from resource file.}
			AddResMenu(MyMenus[AppleM],'DRVR'); {Add desk accessory names to Apple menu.}
			MyMenus[FileM] := GetMenu(FileID); {Read file menu from resource file.}

			FOR i := 1 TO MenuCount DO InsertMenu(MyMenus[i],0); {Install menus in menu bar.}
			DrawMenuBar;
		END;

{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Main
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

	BEGIN {Main}
		{---- Initialization ----}
		InitGraf(@ThePort); {Initialize QuickDraw.}
		InitFonts; {Initialize Font Manager.}
		FlushEvents(EveryEvent,0); {Call OS Event Manager to discard any previous events.}
		InitWindows; {Initialize Window Manager.}
		InitMenus; {Initialize Menu Manager.}
		InitDialogs(NIL); {Initialize Dialog Manager.}
		InitCursor; {Call QuickDraw to make cursor (pointer) an arrow.}
		SetUpMenus; {Set up menus and menu bar.}

		{writeln window init stuff}
		numLines := 200;
		numCharsPerLine := 100;
		WWInit(numLines,numCharsPerLine);		
	  {set up output window}
		with screenBits.Bounds do
			begin
				myLeft := left + 40;
				myRight := right - 100;
				myTop := top + 40;
				myBottom := bottom - 20;
				SetRect(bounds, myLeft, myTop, myRight, myBottom); {l,t,r,b}
			end; {with}
		windowTitle := 'Slot information window';
		goAway := FALSE;
		visible := TRUE;
		outputFont := Courier;
		outputSize := 9;
		WWNew(bounds,windowTitle,goAway,visible,outputFont,outputSize);

		{verify SlotManager is implemented}
		IF (NGetTrapAddress(SlotManagerTrap,OSTrap) <> NGetTrapAddress(UnImplementedTrap,OSTrap)) THEN BEGIN
			{SM implemented, so look at slots, gray menus and report on what cards, resources are installed}
			GrayEmptySlots;

			DisplaySummary;

			QuitFlag := FALSE;

			{---- Main event loop ----}
			REPEAT
				SystemTask;
				IF GetNextEvent(EveryEvent,MyEvent) THEN
					CASE MyEvent.what OF {Event type}
						KeyDown: BEGIN
							theChar := chr(BitAnd(MyEvent.message,charCodeMask)); {get the char}
							IF BitAnd(MyEvent.modifiers,cmdKey) <> 0 THEN {if Command key down, call Menu}
								DoCommand(MenuKey(theChar)) {Manager to learn which}
						END;
						MouseDown:
							CASE FindWindow(MyEvent.where,WhichWindow) OF
								InSysWindow:
									SystemClick(MyEvent,WhichWindow);
								InMenuBar:
									DoCommand(MenuSelect(MyEvent.where))
							END; {Of MouseDown}
					END
			UNTIL QuitFlag
		END
		ELSE BEGIN {no slot manager}
			writeln('Sorry, this machine has no slot manager.');
			writeln('Please try again')
		END
	END.

